package com.icehelper.hades.base.log;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

/**
 * 脱敏处理
 *
 * @author Leon Sun
 */
public class DesensitizationManager {

  private static HadesLogger logger = HadesLogger.create(DesensitizationManager.class);
  public static final String DEFAULT_CONFIG_FILE = "desensitization.yml";
  public static final String UNDERSCORE = "_";

  Map<String, DesensitizationConfig.FieldConfig> nameToFieldConfigMap = new HashMap<>();
  List<Character> escapeTokens = new ArrayList<>();
  List<Character> terminalTokens = new ArrayList<>();
  List<String> desensitizationFields = new ArrayList<>();


  DesensitizationManager() {
    try (InputStream is = DesensitizationManager.class.getClassLoader()
        .getResourceAsStream(DEFAULT_CONFIG_FILE)) {
      DesensitizationConfig config = new ObjectMapper(new YAMLFactory())
          .readValue(is, DesensitizationConfig.class);
      if (null != config.getEscape_tokens()) {
        escapeTokens.addAll(config.getEscape_tokens());
      }
      if (null != config.getTerminal_tokens()) {
        terminalTokens.addAll(config.getTerminal_tokens());
      }
      List<DesensitizationConfig.FieldConfig> fieldConfigList = config.getDesensitization_fields();
      if (CollectionUtils.isEmpty(fieldConfigList)) {
        return;
      }
      for ( DesensitizationConfig.FieldConfig fieldConfig : fieldConfigList) {
        List<DesensitizationConfig.FieldConfig> aliasConfig = flatten(fieldConfig);
        for ( DesensitizationConfig.FieldConfig aliasFieldConfig : aliasConfig) {
          nameToFieldConfigMap.put(aliasFieldConfig.getField_name(), aliasFieldConfig);
          String camelName = underscoreToCamelcase(aliasFieldConfig.getField_name());
          if (StringUtils.equals(camelName, aliasFieldConfig.getField_name())) {
            continue;
          }
          aliasFieldConfig = DesensitizationConfig.FieldConfig.create(aliasFieldConfig).setField_name(camelName);
          nameToFieldConfigMap.put(camelName, aliasFieldConfig);
        }
      }
      desensitizationFields.addAll(nameToFieldConfigMap.keySet());
      desensitizationFields = ImmutableList.copyOf(desensitizationFields);
    } catch (Exception e) {
      logger.error("caught error when initialize desensitization mgr|error=", e);
    }
  }

  public String desensitization(String str, String fieldName) {
    DesensitizationConfig.FieldConfig config = nameToFieldConfigMap.get(fieldName);
    if (null == config) {
      return str;
    }
    if (StringUtils.isEmpty(config.getRegex()) || StringUtils.isEmpty(config.getReplace())) {
      return DigestUtils.md5Hex(str);
    }
    return str.replaceAll(config.getRegex(), config.getReplace());
  }

  private List<DesensitizationConfig.FieldConfig> flatten( DesensitizationConfig.FieldConfig fieldConfig) {
    List<DesensitizationConfig.FieldConfig> result = new ArrayList<>();
    result.add(fieldConfig);

    if (CollectionUtils.isEmpty(fieldConfig.getAlias())) {
      return result;
    }
    for (String alias : fieldConfig.getAlias()) {
      DesensitizationConfig.FieldConfig aliasConfig = DesensitizationConfig.FieldConfig.create(fieldConfig).setField_name(alias);
      result.add(aliasConfig);
    }

    return result;
  }

  protected String underscoreToCamelcase(String input) {
    if (StringUtils.isBlank(input) || !StringUtils.contains(input, UNDERSCORE)) {
      return input;
    }
    List<String> alias = Lists.newArrayList(StringUtils.split(input, UNDERSCORE))
        .stream().filter(s -> StringUtils.isNotBlank(s)).collect(Collectors.toList());
    if (CollectionUtils.isEmpty(alias)) {
      return input;
    }
    Iterator<String> iterator = alias.iterator();
    StringBuilder result = new StringBuilder(iterator.next());
    while (iterator.hasNext()) {
      String str = iterator.next();
      result.append(str.substring(0, 1).toUpperCase());
      result.append(str.substring(1));
    }
    return result.toString();
  }

  public List<String> getDesensitizationFields() {
    return desensitizationFields;
  }

  public boolean isEscapeToken(char c) {
    return escapeTokens.contains(c);
  }

  public boolean isTerminalToken(char c) {
    return terminalTokens.contains(c);
  }


  public static class DesensitizationConfig {

    List<Character> terminal_tokens;
    List<Character> escape_tokens;
    List<FieldConfig> desensitization_fields;

    public DesensitizationConfig() {
    }

    public List<Character> getTerminal_tokens() {
      return terminal_tokens;
    }

    public List<Character> getEscape_tokens() {
      return escape_tokens;
    }

    public List<FieldConfig> getDesensitization_fields() {
      return desensitization_fields;
    }

    public DesensitizationConfig setTerminal_tokens(
        List<Character> terminal_tokens) {
      this.terminal_tokens = terminal_tokens;
      return this;
    }

    public DesensitizationConfig setEscape_tokens(List<Character> escape_tokens) {
      this.escape_tokens = escape_tokens;
      return this;
    }

    public DesensitizationConfig setDesensitization_fields(
        List<FieldConfig> desensitization_fields) {
      this.desensitization_fields = desensitization_fields;
      return this;
    }

    @Override
    public String toString() {
      final StringBuilder sb = new StringBuilder("DesensitizationConfig{");
      sb.append("terminal_tokens=").append(terminal_tokens);
      sb.append(", escape_tokens=").append(escape_tokens);
      sb.append(", desensitization_fields=").append(desensitization_fields);
      sb.append('}');
      return sb.toString();
    }

    public static class FieldConfig {

      String field_name;
      List<String> alias;
      String regex;
      String replace;

      public static FieldConfig create(FieldConfig src) {
        return new FieldConfig(src);
      }

      public FieldConfig() {
      }

      public FieldConfig(FieldConfig src) {
        setField_name(src.getField_name());
        setAlias(src.getAlias());
        setRegex(src.getRegex());
        setReplace(src.getReplace());
      }

      public String getField_name() {
        return field_name;
      }

      public List<String> getAlias() {
        return alias;
      }

      public FieldConfig setField_name(String field_name) {
        this.field_name = field_name;
        return this;
      }

      public FieldConfig setAlias(List<String> alias) {
        this.alias = alias;
        return this;
      }

      public String getRegex() {
        return regex;
      }

      public void setRegex(String regex) {
        this.regex = regex;
      }

      public String getReplace() {
        return replace;
      }

      public void setReplace(String replace) {
        this.replace = replace;
      }

      @Override
      public String toString() {
        final StringBuilder sb = new StringBuilder("FieldConfig{");
        sb.append("field_name='").append(field_name).append('\'');
        sb.append(", alias=").append(alias);
        sb.append('}');
        return sb.toString();
      }
    }

  }
}
